package com.biz.primus.ms.cart.service;

import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.base.vo.StockRespVo;
import com.biz.primus.base.vo.ThreeTuple;
import com.biz.primus.common.utils.CopyUtils;
import com.biz.primus.common.utils.DateUtil;
import com.biz.primus.model.cart.exception.ShopCartException;
import com.biz.primus.model.cart.vo.*;
import com.biz.primus.model.depot.exception.DepotException;
import com.biz.primus.model.stock.vo.MeetStockRespVo;
import com.biz.primus.model.stock.vo.ProductQuantityPair;
import com.biz.primus.model.stock.vo.ProvinceStockReqVo;
import com.biz.primus.model.stock.vo.StocksReqVo;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.cart.dao.redis.ShopCartItemRedisDao;
import com.biz.primus.ms.cart.dao.ro.ShopCartItemRo;
import com.biz.primus.ms.cart.feign.ProductFeignClient;
import com.biz.primus.ms.cart.feign.StockFeignClient;
import com.biz.primus.ms.cart.service.builder.ShopCartBuilder;
import com.biz.primus.ms.cart.service.builder.ShopCartBundleBuilder;
import com.biz.primus.ms.cart.service.builder.ShopCartItemVoBuilder;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.google.common.collect.Lists.newArrayList;

/**
 * ShopCartService 购物车服务
 *
 * @author JKLiues
 * @date 2017/10/24
 */
@Service
@Slf4j
public class ShopCartService extends AbstractBaseService {

    private final static int FIRST_ONE = 0;

    private final ShopCartItemRedisDao shopCartItemRedisDao;
    private final ProductFeignClient productFeignClient;

    @Autowired
    private StockFeignClient stockFeignClient;

    @Autowired
    ShopCartService(ShopCartItemRedisDao shopCartItemRedisDao,
                    ProductFeignClient productFeignClient) {

        this.shopCartItemRedisDao = shopCartItemRedisDao;
        this.productFeignClient = productFeignClient;
    }

    /**
     * 购物车详情
     *
     * @param reqVo 查询VO
     */
    public ShopCartRespVo getShopCartInfo(CartBaseReqVo reqVo) {
        Long userId = reqVo.getUserId();
        AssertUtils.notNull(userId, ShopCartException.USER_ID_IS_NULL);
        AssertUtils.isTrue(reqVo.hasLocation(), DepotException.DEPOT_LAT_OR_LON_IS_NULL);
        ShopCartRespVo shopCartVo = new ShopCartRespVo();
        List<ShopCartItemRo> shopCartItemRos = shopCartItemRedisDao.findByUserId(userId);
        log.debug("购物车商品为{}.", shopCartItemRos);
        if (CollectionUtils.isEmpty(shopCartItemRos)) {
            return shopCartVo;
        }
        List<String> productCodes = shopCartItemRos.stream().map(ShopCartItemRo::getProductCode)
                .collect(Collectors.toList());
        ShopCartProductReqVo vo = new ShopCartProductReqVo();
        CopyUtils.copyProperties(reqVo, vo);
        vo.setProductCodes(productCodes);
        List<ShopCartProductRespVo> shopCartProductRespVos = productFeignClient.shoppingCartProducts(vo);
        ShopCartBuilder shopCartBuilder = ShopCartBuilder.createBuilder();
        ShopCartBundleVo fast = ShopCartBundleBuilder.createBuilder()
                .build();
        ShopCartBundleVo normal = ShopCartBundleBuilder.createBuilder()
                .build();
        ShopCartBundleVo invalidate = ShopCartBundleBuilder.createBuilder()
                .build();
        Boolean fastMode = reqVo.getFastMode();
        Map<String, ShopCartItemRo> productCodeToCartItemRo = this.mapProductCodeToShopCartItemRo(shopCartItemRos);
        for (ShopCartProductRespVo cartProductVo : shopCartProductRespVos) {
            ShopCartItemRo shopCartItemRo = productCodeToCartItemRo.get(cartProductVo.getProductCode());
            ThreeTuple<ShopCartItemVo, ShopCartItemVo, ShopCartItemVo> threeTuple =
                    this.buildTuple(shopCartItemRo.getQuantity(), shopCartItemRo.isSelected(), cartProductVo, fastMode);
            this.addShopCartItemToCartBundle(fast, threeTuple.getFirst());
            this.addShopCartItemToCartBundle(normal, threeTuple.getSecond());
            this.addShopCartItemToCartBundle(invalidate, threeTuple.getThird());
        }
        return shopCartBuilder.addCartBundle(fast, 0)
                .addCartBundle(normal, 1)
                .addCartBundle(invalidate, 2)
                .build();
    }

    /**
     * 加入购物车
     *
     * @param vo 请求对象VO
     */
    public ShopCartRespVo addShopCartItem(ShopCartItemAddVo vo) {
        Boolean fastMode = vo.getFastMode();
        Long userId = vo.getUserId();
        AssertUtils.notNull(userId, ShopCartException.USER_ID_IS_NULL);
        String productCode = vo.getProductCode();
        AssertUtils.hasLength(productCode, ShopCartException.PRODUCT_CODE_IS_NULL);
        int quantityToAdd = vo.getQuantity();
        AssertUtils.isTrue(quantityToAdd > 0, ShopCartException.PRODUCT_QUANTITY_LT_ZERO);
        String depotCode = vo.getDepotCode();
        AssertUtils.hasLength(depotCode, ShopCartException.DEPOT_CODE_IS_NULL);
        StocksReqVo stockReqVo = new StocksReqVo();
        stockReqVo.setDepotCode(depotCode);
        stockReqVo.setProductCodes(Lists.newArrayList(productCode));
        List<StockRespVo> depotStocks = stockFeignClient.getDepotsStockInBatch(stockReqVo);
        AssertUtils.notEmpty(depotStocks, ShopCartException.PRODUCT_NOT_FOUNT);
        StockRespVo stockObject = depotStocks.get(FIRST_ONE);
        AssertUtils.notNull(stockObject, ShopCartException.PRODUCT_NOT_FOUNT);
        Integer depotQuantity = stockObject.getQuantity();
        Integer warehouseQuantity = stockObject.getWarehouseQuantity();
        ShopCartItemRo shopCartItemRo = shopCartItemRedisDao.findByUserIdAndProductCode(userId, productCode);
        if (shopCartItemRo == null) {
            shopCartItemRo = new ShopCartItemRo();
            shopCartItemRo.setProductCode(productCode);
            shopCartItemRo.setUserId(userId);
        }
        Integer originQuantity = shopCartItemRo.getQuantity();
        Integer quantity = quantityToAdd + originQuantity;
        if (fastMode) {
            AssertUtils.isTrue(quantity <= depotQuantity, ShopCartException.PRODUCT_STOCK_NOT_ENOUGH);
        } else {
            AssertUtils.isTrue(quantity <= warehouseQuantity, ShopCartException.PRODUCT_STOCK_NOT_ENOUGH);
        }
        shopCartItemRo.setUpdateTimestamp(DateUtil.now());
        shopCartItemRo.setQuantity(quantity);
        shopCartItemRo.setSelected(true);
        shopCartItemRedisDao.save(shopCartItemRo);
        return this.getShopCartInfo(vo);
    }


    /**
     * 删除购物车中的指定商品
     *
     * @param reqVo 请求VO
     */
    public void deleteShopCartItems(CartProductListVo reqVo) {
        Long userId = reqVo.getUserId();
        AssertUtils.notNull(userId, ShopCartException.USER_ID_IS_NULL);
        if (CollectionUtils.isEmpty(reqVo.getProductCodes())) {
            return;
        }
        shopCartItemRedisDao.deleteByUserIdAndProductCodes(userId, reqVo.getProductCodes());
    }

    /**
     * 选中购物车中的商品
     *
     * @param vo 请求Vo
     */
    public void selectedItem(ShopCartItemCheckVo vo) {
        Long userId = vo.getUserId();
        AssertUtils.notNull(userId, ShopCartException.USER_ID_IS_NULL);
        if (CollectionUtils.isNotEmpty(vo.getProductCodes()) && vo.getSelected() != null) {
            List<ShopCartItemRo> shopCartItemRos = shopCartItemRedisDao.findByUserId(userId);
            if (CollectionUtils.isNotEmpty(shopCartItemRos)) {
                List<ShopCartItemRo> selectedShopCartItemRos = newArrayList();
                for (ShopCartItemRo shopCartItemRo : shopCartItemRos) {
                    if (vo.getProductCodes().contains(shopCartItemRo.getProductCode())) {
                        shopCartItemRo.setSelected(vo.getSelected());
                        selectedShopCartItemRos.add(shopCartItemRo);
                    }
                }
                shopCartItemRedisDao.save(selectedShopCartItemRos);
            }
        }
    }


    /**
     * 获取数量
     */
    public ShopCartNumRespVo getShopCartNum(CartBaseReqVo reqVo) {
        Long userId = reqVo.getUserId();
        AssertUtils.notNull(userId, ShopCartException.USER_ID_IS_NULL);
        List<ShopCartItemRo> shopCartItemRos = shopCartItemRedisDao.findByUserId(userId);
        if (CollectionUtils.isEmpty(shopCartItemRos)) {
            return new ShopCartNumRespVo();
        }
        Map<String, Boolean> selectMap = Optional.of(shopCartItemRos).orElse(new ArrayList<>())
                .stream().collect(Collectors.toMap(ShopCartItemRo::getProductCode, ShopCartItemRo::isSelected));
        ShopCartProductReqVo req = new ShopCartProductReqVo();
        CopyUtils.copyProperties(reqVo, req);
        req.setProductCodes(Lists.newArrayList(selectMap.keySet()));
        log.debug("\n\n\n\n获取购物车信息请求参数：\n\n{}\n\n\n\n", req);
        List<ShopCartProductRespVo> shopCartProductRespVos = productFeignClient.shoppingCartProducts(req);
        Map<String, ShopCartNumRespVo> map = new HashMap<>(shopCartProductRespVos.size());
        for (ShopCartProductRespVo itemVo : shopCartProductRespVos) {
            String productCode = itemVo.getProductCode();
            ShopCartNumRespVo numRespVo = map.getOrDefault(productCode, null);
            if (numRespVo == null) {
                numRespVo = new ShopCartNumRespVo();
            }
            Boolean selected = selectMap.getOrDefault(productCode, null);
            if (selected == null) {
                continue;
            }
            if (selected) {
                numRespVo.addSelectNum(itemVo.getQuantity());
            }
            numRespVo.addTotalNum(itemVo.getQuantity());
            map.put(productCode, numRespVo);
        }
        int totalNum = 0;
        int selectedNum = 0;
        for (Map.Entry<String, ShopCartNumRespVo> entriy : map.entrySet()) {
            ShopCartNumRespVo value = entriy.getValue();
            totalNum += value.getTotalNum();
            selectedNum += value.getSelectNum();
        }
        return new ShopCartNumRespVo(totalNum, selectedNum);
    }

    /**
     * 验证有无满足同时效满足商品库存的门店
     */
    public MeetStockRespVo validateMeetQuantity(CartBaseReqVo reqVo) {
        Long userId = reqVo.getUserId();
        AssertUtils.notNull(userId, ShopCartException.USER_ID_IS_NULL);
        List<ShopCartItemRo> shopCartItemRos = shopCartItemRedisDao.findByUserId(userId);
        List<ProductQuantityPair> pair = Optional.ofNullable(shopCartItemRos).orElse(new ArrayList<>())
                .stream()
                .filter(ShopCartItemRo::isSelected)
                .map(ro -> new ProductQuantityPair(ro.getProductCode(), ro.getQuantity()))
                .collect(Collectors.toList());
        ProvinceStockReqVo vo = new ProvinceStockReqVo();
        CopyUtils.copyProperties(reqVo, vo);
        vo.setPair(pair);
        return stockFeignClient.getMeetStock(vo);
    }

    /**
     * 走结算页
     */
    public void goSettlePage(CartBaseReqVo reqVo) {

    }

    /**
     * 根据购物车商品数量获取门店和酒库item,
     * 若数量满足最近门店数量, 则返回就近门店ShopCartItemVo,
     * 反之，返回就近门店+酒库ShopCartItemVo
     *
     * @param cartProductVo 商品信息vo
     * @param checked       是否选中
     * @param fastMode      是否立即送
     * @param quantity      购物车数量
     */
    private ThreeTuple<ShopCartItemVo, ShopCartItemVo, ShopCartItemVo> buildTuple(int quantity, boolean
            checked, ShopCartProductRespVo cartProductVo, boolean fastMode) {
        ShopCartItemVo depotShopCartItemVo;
        ShopCartItemVo warehouseShopCartItemVo = null;
        ShopCartItemVo invalidShopCartItemVo = null;
        Integer quantityInDepot = cartProductVo.getQuantity();
        Integer quantityInWarehouse = cartProductVo.getWarehouseQuantity();
        Integer marketPrice = cartProductVo.getMarketPrice();
        Integer finalPrice = cartProductVo.getFinalPrice();
        if (fastMode) {
            if (quantityInDepot < quantity) {
                depotShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(checked)
                        .setQuantity(quantityInDepot)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
                invalidShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(false)
                        .setQuantity(quantity - quantityInDepot)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
            } else {
                depotShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(checked)
                        .setQuantity(quantity)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
            }
        } else {
            if (quantityInDepot >= quantity) {
                depotShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(checked)
                        .setQuantity(quantity)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
            } else if (quantityInDepot < quantity && quantity <= quantityInWarehouse + quantityInDepot) {
                depotShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(checked)
                        .setQuantity(quantityInDepot)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
                warehouseShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(checked)
                        .setQuantity(quantity - quantityInDepot)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
            } else {
                depotShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(checked)
                        .setQuantity(quantityInDepot)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
                warehouseShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(checked)
                        .setQuantity(quantityInWarehouse)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
                invalidShopCartItemVo = ShopCartItemVoBuilder.createBuilder(cartProductVo)
                        .setSelected(false)
                        .setQuantity(quantity - quantityInDepot - quantityInWarehouse)
                        .setMarketPrice(marketPrice)
                        .setFinalPrice(finalPrice)
                        .build();
            }
        }
        return new ThreeTuple<>(depotShopCartItemVo, warehouseShopCartItemVo, invalidShopCartItemVo);
    }


    /**
     * 添加购物车item到购物车bundle
     */
    private void addShopCartItemToCartBundle(ShopCartBundleVo cartBundleVo, ShopCartItemVo shopCartItemVo) {
        if (cartBundleVo == null || shopCartItemVo == null) {
            return;
        }
        List<ShopCartItemVo> items = cartBundleVo.getItems();
        if (items == null) {
            items = newArrayList(shopCartItemVo);
        } else {
            items.add(shopCartItemVo);
        }
        cartBundleVo.setTotalAmount(cartBundleVo.getTotalAmount() + shopCartItemVo.getAmount());
        cartBundleVo.setTotalPrice(cartBundleVo.getTotalPrice() + shopCartItemVo.getPrice());
        cartBundleVo.setItems(items);
    }

    /**
     * 映射 productCode to ShopCartItemRo
     */
    private Map<String, ShopCartItemRo> mapProductCodeToShopCartItemRo(List<ShopCartItemRo> shopCartItemRos) {
        return Optional.ofNullable(shopCartItemRos).orElse(new ArrayList<>()).stream()
                .filter(Objects::nonNull)
                .filter(ro -> StringUtils.isNotBlank(ro.getProductCode()))
                .collect(Collectors.toMap(ShopCartItemRo::getProductCode, ro -> ro));
    }
}
